package org.balthie.demo.jdk.util.concurrent.locks.stampedlock;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.StampedLock;

//https://www.liaoxuefeng.com/wiki/1252599548343744/1309138673991714
//StampedLock和ReadWriteLock相比，改进之处在于：读的过程中也允许获取写锁后写入！这样一来，我们读的数据就可能不一致，所以，需要一点额外的代码来判断读的过程中是否有写入，这种读锁是一种乐观锁。
//把读锁细分为乐观读和悲观读，还提供了更复杂的将悲观读锁升级为写锁的功能
public class StampedLockDemo {
	private final StampedLock stampedLock = new StampedLock();

	private double x;
	private double y;
	
	public void move(double deltaX, double deltaY) {
		long stamp = stampedLock.writeLock(); // 获取写锁
		System.out.println(String.format("[%s] writeLock [%s]", Thread.currentThread(), stamp));
		long duration = (long) (Math.random() * 100);
		try {
			x += deltaX;
			y += deltaY;
//			模拟写入耗时
			TimeUnit.MILLISECONDS.sleep(duration);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			stampedLock.unlockWrite(stamp); // 释放写锁
			System.out.println(String.format("[%s] unlockWrite [%s] duration[%s]", Thread.currentThread(), stamp, duration));
		}
	}

	public double distanceFromOrigin() {
		long stamp = stampedLock.tryOptimisticRead(); // 获得一个乐观读锁
		// 注意下面两行代码不是原子操作
		// 假设x,y = (100,200)
		double currentX = x;
		// 此处已读取到x=100，但x,y可能被写线程修改为(300,400)
		double currentY = y;
		// 此处已读取到y，如果没有写入，读取是正确的(100,200)
		// 如果有写入，读取是错误的(100,400)
		if (!stampedLock.validate(stamp)) { // 检查乐观读锁后是否有其他写锁发生
			stamp = stampedLock.readLock(); // 获取一个悲观读锁
			System.out.println(String.format("[%s] readLock [%s]", Thread.currentThread(), stamp));
			try {
				currentX = x;
				currentY = y;
			} finally {
				stampedLock.unlockRead(stamp); // 释放悲观读锁
				System.out.println(String.format("[%s] unlockRead [%s]", Thread.currentThread(), stamp));
			}
		}
		else
		{
			System.out.println(String.format("[%s] tryOptimisticRead [%s]", Thread.currentThread(), stamp));
		}
		return Math.sqrt(currentX * currentX + currentY * currentY);
	}
}
